package cn.sciento.boot.iam.field.handler;


import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.sciento.core.oauth.CustomUserDetails;
import cn.sciento.core.oauth.DetailsHelper;
import org.apache.commons.lang3.reflect.FieldUtils;
import cn.sciento.boot.iam.field.config.FieldPermissionProperty;
import cn.sciento.boot.iam.field.dto.FieldPermission;
import cn.sciento.core.redis.RedisHelper;
import cn.sciento.mybatis.domian.SecurityToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

public class ResetFieldHandler implements FieldPermissionHandler {
  private static final Logger logger = LoggerFactory.getLogger(ResetFieldHandler.class);
  private static final String TOKEN_CACHE_PREFIX = "hiam:field-permission:token:";
  private RedisHelper redisHelper;
  private FieldPermissionProperty fieldPermissionProperty;

  public ResetFieldHandler(RedisHelper redisHelper, FieldPermissionProperty fieldPermissionProperty) {
    this.redisHelper = redisHelper;
    this.fieldPermissionProperty = fieldPermissionProperty;
  }

  public int getOrder() {
    return -2147483648;
  }

  public void beforeProcess(List<FieldPermission> fieldPermissionList, Object[] args) {
    if (args != null) {
      Object[] var3 = args;
      int var4 = args.length;

      for(int var5 = 0; var5 < var4; ++var5) {
        Object arg = var3[var5];
        if (arg == null) {
          return;
        }

        if (arg instanceof Collection) {
          this.beforeProcess(fieldPermissionList, ((Collection)arg).toArray());
          return;
        }

        if (arg instanceof SecurityToken) {
          String token = ((SecurityToken)arg).get_token();
          if (StringUtils.hasText(token)) {
            String cacheValue = this.redisHelper.strGet(this.getTokenCacheKey(token));
            if (!StringUtils.isEmpty(cacheValue)) {
              try {
                this.resetValue(arg.getClass(), arg, cacheValue);
              } catch (IllegalAccessException | IOException var10) {
                logger.error("Error read cache value.", var10);
              }
            }
          }
        }
      }
    }

  }

  private <T> void resetValue(Class<?> clazz, T obj, String json) throws IOException, IllegalAccessException {
    Iterator<String> fieldNames = RedisHelper.getObjectMapper().readTree(json).fieldNames();
    Object value = RedisHelper.getObjectMapper().readValue(json, clazz);

    while(fieldNames.hasNext()) {
      String fieldName = (String)fieldNames.next();
      FieldUtils.writeDeclaredField(obj, fieldName, FieldUtils.readDeclaredField(value, fieldName, true), true);
    }

  }

  public void afterProcess(List<FieldPermission> fieldPermissionList, Object arg) {
    if (arg != null) {
      if (arg instanceof ResponseEntity) {
        this.afterProcess(fieldPermissionList, ((ResponseEntity)arg).getBody());
      } else if (!(arg instanceof Collection)) {
        if (!CollectionUtils.isEmpty(fieldPermissionList) && arg instanceof SecurityToken) {
          String token = ((SecurityToken)arg).get_token();
          if (StringUtils.hasText(token)) {
            this.handler((String)null, (SecurityToken)arg, this.getTokenCacheKey(token), fieldPermissionList);
          }

        }
      } else {
        Iterator var3 = ((Collection)arg).iterator();

        while(var3.hasNext()) {
          Object item = var3.next();
          this.afterProcess(fieldPermissionList, item);
        }

      }
    }
  }

  private void handler(String parentFieldName, SecurityToken obj, String tokenKey, List<FieldPermission> fieldPermissionList) {
    Map<String, Object> cacheMap = new HashMap(fieldPermissionList.size());
    Map<String, Field> fieldMap = (Map)this.getAllFieldWithoutStaticAndFinal(obj.getClass()).stream().collect(Collectors.toMap((item) -> {
      return this.getFieldName(parentFieldName, item.getName());
    }, Function.identity()));
    Iterator var7 = fieldPermissionList.iterator();

    while(var7.hasNext()) {
      FieldPermission fieldPermission = (FieldPermission)var7.next();
      if (fieldMap.containsKey(fieldPermission.getFieldName())) {
        try {
          cacheMap.put(fieldPermission.getFieldName(), FieldUtils.readField((Field)fieldMap.get(fieldPermission.getFieldName()), obj, true));
        } catch (IllegalAccessException var11) {
          logger.error("Error read field value.", var11);
        }
      }
    }

    var7 = fieldMap.values().iterator();

    while(var7.hasNext()) {
      Field field = (Field)var7.next();
      if (SecurityToken.class.isAssignableFrom(field.getType())) {
        try {
          SecurityToken securityToken = (SecurityToken)FieldUtils.readField(field, obj, true);
          if (securityToken != null && StringUtils.hasText(securityToken.get_token())) {
            this.handler(field.getName(), securityToken, this.getTokenCacheKey(securityToken.get_token()), fieldPermissionList);
          }
        } catch (IllegalAccessException var10) {
          logger.error("Error read field value.", var10);
        }
      }
    }

    if (!CollectionUtils.isEmpty(cacheMap)) {
      this.redisHelper.objectSet(tokenKey, cacheMap);
      this.redisHelper.setExpire(tokenKey, this.fieldPermissionProperty.getExpire());
    }

  }

  private String getTokenCacheKey(String token) {
    CustomUserDetails userDetails = DetailsHelper.getUserDetails();
    Assert.notNull(userDetails, "UserDetails is null.");
    return "hiam:field-permission:token:" + token + ":" + userDetails.getUserId() + "." + userDetails.getRoleId();
  }

  private String getFieldName(String parentFieldName, String fieldName) {
    return parentFieldName != null ? parentFieldName + "." + fieldName : fieldName;
  }

  private List<Field> getAllFieldWithoutStaticAndFinal(Class<?> clazz) {
    ArrayList fieldList;
    for(fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()) {
      Field[] declaredFields = clazz.getDeclaredFields();
      Field[] var4 = declaredFields;
      int var5 = declaredFields.length;

      for(int var6 = 0; var6 < var5; ++var6) {
        Field field = var4[var6];
        if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
          fieldList.add(field);
        }
      }
    }

    return fieldList;
  }
}
